# -*-coding: Utf-8 -*-
# @File : exception .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time：2024/7/1
from rest_framework.views import exception_handler
from rest_framework.response import Response
import logging
from typing import Optional
from common import BaseErrorCode

logger = logging.getLogger('django')


# 抽象异常
class AbstractException(Exception):
    def __init__(self,
                 message: Optional[str] = None,
                 cause: Exception = None,
                 error_type=None):
        '''
        基础的异常类型
        :param message: 异常错误信息
        :param cause: 异常引起类型
        :param error_type: 异常类型 UserErrorCode.USER_USERNAME_NULL.code()
        '''
        # 调用 Python 自带的异常类型抛出异常
        # 如果 错误信息有值的情况下抛出 Python 自带的异常
        # 如果没有则调用父类的 捕获方法 抛出未知异常
        super().__init__(message) if message else super().__init__()
        # 如果有异常类型
        if cause:
            # 做一个异常类型的属性
            self.__cause__ = cause
        # 如果有异常状态码 则 保存异常状态码
        self.error_code = error_type.code() if error_type else None
        # 如果有异常错误信息 则 保存异常错误信息
        # 否则就是 异常状态码类的异常信息
        self.error_message = message if message else (error_type.message() if error_type else None)

    # 抛出自建异常信息
    def __str__(self):
        return f"AbstractException{{code='{self.error_code}', message='{self.error_message}'}}"


# 客户端异常
class ClientException(AbstractException):
    def __init__(self,
                 message: Optional[str] = None,
                 cause: Exception = None,
                 error_type=None):
        '''
        基础的异常类型
        :param message: 异常错误信息
        :param cause: 异常引起类型
        :param error_type: 异常类型 UserErrorCode.USER_USERNAME_NULL.code()
        '''
        super().__init__(message=message, cause=cause,
                         error_type=error_type if error_type else BaseErrorCode.CLIENT_ERROR)

    def __str__(self):
        return f"ClientException{{code='{self.error_code}', message='{self.error_message}'}}"


# 远程调用服务异常
class RemoteException(AbstractException):
    def __init__(self,
                 message: Optional[str] = None,
                 cause: Exception = None,
                 error_type=BaseErrorCode.REMOTE_ERROR):
        '''
        基础的异常类型
        :param message: 异常错误信息
        :param cause: 异常引起类型
        :param error_type: 异常类型 UserErrorCode.USER_USERNAME_NULL.code()
        '''
        super().__init__(message=message, cause=cause, error_type=error_type)

    def __str__(self):
        return f"RemoteException{{code='{self.error_code}', message='{self.error_message}'}}"


# 服务端异常
class ServiceException(AbstractException):
    def __init__(self,
                 message: Optional[str] = None,
                 cause: Exception = None,
                 error_type=BaseErrorCode.SERVICE_ERROR
                 ):
        actual_message = message if message is not None else error_type.message()
        super().__init__(actual_message, cause, error_type)

    # 抛出自定义异常信息
    def __str__(self):
        return f"ServiceException{{code='{self.error_code}', message='{self.error_message}'}}"


class GlobalException(object):
    def __init__(self):
        ...


# 全局异常处理函数
def global_exception_handler(exc, context):
    '''

    :param exc: 当前的异常类型
    :param context: 上下文对象
    :return:
    '''
    # 【一】从当前的 上下文 对象中 获取到 request 请求对象
    request = context.get('request')
    # 【二】从当前的上下文对象中获取到 view 视图对象
    view = context.get('view')
    # 【三】从当前的 request 对象中获取到当前的请求IP地址
    ip = request.META.get('REMOTE_ADDR')
    # 【四】异常捕获校验当前用户是否登录
    try:
        # 获取当前登录的用户id (如果当前登录的状态下)
        user_id = request.user.pk
    # 未登录用户则使用 匿名用户
    except Exception as e:
        user_id = 'AnonymousUser'
    # 【五】从当前请求中获取到当前请求访问的目标地址
    path = request.get_full_path()
    # 【六】将视图类的名字变为字符串 ---> 保存
    view_str = str(view)
    # 【七】调用 DRF 原本的 异常对象处理类 处理当前异常
    # 如果是 DRF 的异常则会返回 DRF 的异常信息
    # 如果不是 DRF 的异常信息则返回 空值
    res = exception_handler(exc, context)
    # 【八】记录当前错误的日志信息
    logger.error(
        f'当前用户地址::>> {ip} | 当前用户id号::>> {user_id} | 当前请求地址::>>{path} | 访问的视图函数::>> {view_str} | 错误信息::>> {str(exc)}')
    # 【九】获取 DRF 的异常报错信息 : 两种提取方式
    if res:
        if isinstance(res.data, dict):
            # 【1】方式一 ：从res.data这个字典的detail中取
            data = {'code': 999, 'msg': res.data.get('detail', '系统错误，请联系系统管理员')}
        else:
            # 【2】方式二 ： 直接取data
            data = {'code': 998, 'msg': res.data}
    # 【十】Django 自带的异常信息
    else:
        data = {'code': 888, 'msg': str(exc)}
    # 【十一】返回 DRF 的响应对象
    return Response(data)
